
#ifndef _ENV_H_
#define _ENV_H_


#include <inc/types.h>
#include <inc/queue.h>
#include <inc/traps.h>
#include <inc/mmu.h>


#define LOG2NENV        10
#define NENV            (1<<LOG2NENV)
#define ENVX(envid)     ((envid) & (NENV - 1))
#define GET_ENV_ASID(envid) (((envid)>> 11)<<6)

// Values of env_status in struct Env
#define ENV_FREE        0
#define ENV_RUNNABLE            1
#define ENV_NOT_RUNNABLE        2

struct Env {
        struct Trapframe env_tf;        // Saved registers
        LIST_ENTRY(Env) env_link;       // Free list
        u_int env_id;                   // Unique environment identifier
        u_int env_parent_id;            // env_id of this env's parent
        u_int env_status;               // Status of the environment
        Pde  *env_pgdir;                // Kernel virtual address of page dir
        u_int env_cr3;
        LIST_ENTRY(Env) env_sched_link;

        // Lab 4 IPC
        u_int env_ipc_value;            // data value sent to us
        u_int env_ipc_from;             // envid of the sender
        u_int env_ipc_recving;          // env is blocked receiving
        u_int env_ipc_dstva;            // va at which to map received page
        u_int env_ipc_perm;             // perm of page mapping received

        // Lab 4 fault handling
        u_int env_pgfault_handler;      // page fault state
        u_int env_xstacktop;            // top of exception stack

        // Lab 6 scheduler counts
        u_int env_runs;                 // number of times been env_run'ed
};

LIST_HEAD(Env_list, Env);
extern struct Env *envs;                // All environments
extern struct Env *curenv;              // the current env
extern struct Env_list env_sched_list[2]; // runnable env list

void env_init(void);
int env_alloc(struct Env **e, u_int parent_id);
void env_free(struct Env *);
void env_create_priority(u_char *binary, int size, int priority);
void env_create(u_char *binary, int size);
void env_destroy(struct Env *e);

int envid2env(u_int envid, struct Env **penv, int checkperm);
void env_run(struct Env *e);
void env_check();

// for the grading script
#define ENV_CREATE2(x, y) \
{ \
        extern u_char x[], y[]; \
        env_create(x, (int)y); \
}

#define ENV_CREATE_PRIORITY(x, y) \
{\
        extern u_char binary_##x##_start[]; \
        extern u_int binary_##x##_size;\
        env_create_priority(binary_##x##_start, \
                (u_int)binary_##x##_size, y);\
}
#define ENV_CREATE(x) \
{ \
        extern u_char binary_##x##_start[];\
        extern u_int binary_##x##_size; \
        env_create(binary_##x##_start, \
                (u_int)binary_##x##_size); \
}

#endif // !_ENV_H_
